using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Pegasus.Windows.Forms.Explorer
{
	internal class FormsExplorerTree : TreeView, IExplorerTree
	{
		// Local Instance Values
		private Dictionary<string, TreeNode> m_explorerNodes = new Dictionary<string, TreeNode>();
	
		// Local Const Values
		private const string DUMMY_CHILD_NODE = "[Children]";

		/// <summary>
		/// Initializes a new instance of the <see cref="FormsExplorerTree"/> class.
		/// </summary>
		public FormsExplorerTree()
		{
		}
		/// <summary>
		/// Gets or sets the selected tree node.
		/// </summary>
		/// <value>The selected tree node.</value>
		public ExplorerNode SelectedTreeNode
		{
			get
			{
				TreeNode tNode = SelectedNode;
				if( tNode != null )
				{
					return (ExplorerNode) tNode.Tag;
				}

				return null;
			}

			set
			{
				if( value != null && m_explorerNodes.ContainsKey( value.Key ) )
				{
					SelectedNode = m_explorerNodes[ value.Key ];
				}
				else
				{
					SelectedNode = null;
				}
			}
		}

		/// <summary>
		/// Adds the node.
		/// </summary>
		/// <param name="node">The node.</param>
		public void AddNode( ExplorerNode node )
		{
			if( !m_explorerNodes.ContainsKey( node.Key ) )
			{
				if( node.ShowInTree )
				{
					TreeNode tNode = new TreeNode();
					tNode.Tag = node;
					tNode.Text = node.Text;
					tNode.ImageIndex = node.ImageIndex;
					tNode.SelectedImageIndex = node.ImageIndex;

					m_explorerNodes.Add( node.Key, tNode );

					if( node.Parent != null )
					{
						// Get the parent node and if it has a dummy node then
						// clear the node and mark the node has having it's 
						// children loadded.
						TreeNode tParentNode = m_explorerNodes[ node.Parent.Key ];
						if( HasDummyChild( tParentNode ) )
						{
							tParentNode.Nodes.Clear();
						}

						// Add the node to it's parent
						tParentNode.Nodes.Add( tNode );
						node.Parent.ChildrenLoadded = true;
					}
					else
					{
						// This is a root node so add it to our collection
						Nodes.Add( tNode );
					}

					// If we need to load the children on demand then create
					// a dummy node so that we have a '+' button to open.
					if( node.LoadChildrenOnDemand )
					{
						TreeNode tNodeDummy = new TreeNode();
						tNodeDummy.Tag = DUMMY_CHILD_NODE;
						tNodeDummy.Text = DUMMY_CHILD_NODE;

						tNode.Nodes.Add( tNodeDummy );
					}
				}
			}
			else
			{
				throw new ExplorerViewException( "Node with Key already exist in the ExplorerView." );
			}
		}

		/// <summary>
		/// Removes the node.
		/// </summary>
		/// <param name="node">The node.</param>
		public void RemoveNode( ExplorerNode node )
		{
			if( m_explorerNodes.ContainsKey( node.Key ) )
			{
				TreeNode tNode = m_explorerNodes[ node.Key ];
				tNode.Remove();
				m_explorerNodes.Remove( node.Key );
			}
		}

		/// <summary>
		/// Removes the node children.
		/// </summary>
		/// <param name="parentNode">The parent node.</param>
		public void RemoveNodeChildren( ExplorerNode parentNode )
		{
			if( m_explorerNodes.ContainsKey( parentNode.Key ) )
			{
				TreeNode tNode = m_explorerNodes[ parentNode.Key ];
				TreeNode[] tChildNodes = new TreeNode[ tNode.Nodes.Count ];
				tNode.Nodes.CopyTo( tChildNodes, 0 );

				foreach( TreeNode tChildNode in tChildNodes )
				{
					RemoveNode( (ExplorerNode) tChildNode.Tag );
				}
			}
		}

		/// <summary>
		/// Gets the node by key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns>The explorer node or null if the node is not found.</returns>
		public ExplorerNode GetNodeByKey( string key )
		{
			if( m_explorerNodes.ContainsKey( key ) )
			{
				return (ExplorerNode) m_explorerNodes[ key ].Tag;
			}

			return null;
		}

		/// <summary>
		/// Gets the node by path.
		/// </summary>
		/// <param name="path">The path.</param>
		/// <returns>The explorer node or null if the node is not found.</returns>
		public ExplorerNode GetNodeByPath( string path )
		{
			TreeNode tNode = null;

			string[] nodeNames = path.Trim( '\\' ).Split( '\\' );
			TreeNodeCollection subNodes = Nodes;

			foreach( string name in nodeNames )
			{
				tNode = GetTreeNodeByName( subNodes, name );
				if( tNode != null )
				{
					ExplorerNode node = (ExplorerNode) tNode.Tag;
					if( !node.ChildrenLoadded )
					{
						node.LoadChildNodes();
					}

					subNodes = tNode.Nodes;
				}
				else
				{
					return null;
				}
			}

			return (ExplorerNode) tNode.Tag;
		}

		/// <summary>
		/// Sets the image list for the icons in the tree.
		/// </summary>
		/// <param name="imageList">The image list.</param>
		public void SetImageList( ImageList imageList )
		{
			ImageList = imageList;
		}

		/// <summary>
		/// Raises the <see cref="E:AfterSelect"/> event.
		/// </summary>
		/// <param name="args">The <see cref="System.Windows.Forms.TreeViewEventArgs"/> instance containing the event data.</param>
		protected override void OnAfterSelect( TreeViewEventArgs args )
		{
			base.OnAfterSelect( args );
			if( AfterSelect != null )
			{
				AfterSelect( this, new ExplorerEventArgs( (ExplorerNode) args.Node.Tag ) );
			}
		}

		/// <summary>
		/// Raises the <see cref="E:BeforeExpand"/> event.
		/// </summary>
		/// <param name="args">The <see cref="System.Windows.Forms.TreeViewCancelEventArgs"/> instance containing the event data.</param>
		protected override void OnBeforeExpand( TreeViewCancelEventArgs args )
		{
			base.OnBeforeExpand( args );
			if( !args.Cancel )
			{
				TreeNode tNode = args.Node;
				if( HasDummyChild( tNode ) )
				{
					tNode.Nodes.Clear();
					ExplorerNode node = tNode.Tag as ExplorerNode;
					if( node != null )
					{
						node.LoadChildNodes();
					}
				}
			}
		}

		/// <summary>
		/// Determines whether [has dummy child] [the specified node].
		/// </summary>
		/// <param name="node">The node.</param>
		/// <returns>
		/// 	<c>true</c> if [has dummy child] [the specified node]; otherwise, <c>false</c>.
		/// </returns>
		protected bool HasDummyChild( TreeNode node )
		{
			if( node.Nodes.Count == 1 && node.Nodes[ 0 ].Tag is string )
			{
				if( ( (string) node.Nodes[ 0 ].Tag ) == DUMMY_CHILD_NODE )
				{
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Gets the name of the tree node by.
		/// </summary>
		/// <param name="nodes">The nodes.</param>
		/// <param name="name">The name.</param>
		/// <returns></returns>
		protected TreeNode GetTreeNodeByName( TreeNodeCollection nodes, string name )
		{
			foreach( TreeNode node in nodes )
			{
				if( node.Text == name )
				{
					return node;
				}
			}

			return null;
		}

		/// <summary>
		/// Occurs after the tree node is selected.
		/// </summary>
		public new event EventHandler<ExplorerEventArgs> AfterSelect;

	}
}
